//-------------------------------------------------------------------
//	Copyright (c) 2013  Zhirnov Andrey
//	This file is part of the "Noise Library" project.
//	See copyright notice in "linNoiseCL.h".
//-------------------------------------------------------------------

#pragma once

#include "CSourcePerlin.h"

namespace Noise
{
	
    const real DEFAULT_TURBULENCE_FREQUENCY = DEFAULT_PERLIN_FREQUENCY;
    const real DEFAULT_TURBULENCE_POWER = 1.0;
    const int DEFAULT_TURBULENCE_ROUGHNESS = 3;
    const int DEFAULT_TURBULENCE_SEED = DEFAULT_PERLIN_SEED;

	
	//
	// Function Source of randomly displace for perlin noise.
	//

	class CSourceTurbulence : public CSource
	{
	// types
	protected:
		typedef CSourceTurbulence	Self;


	// variables
	protected:
		CSourcePtr		_source;
		real			_power;		// The power (scale) of the displacement.
		CSourcePerlin	_xDistort;	// Noise module that displaces the @a x coordinate.
		CSourcePerlin	_yDistort;	// Noise module that displaces the @a y coordinate.
		CSourcePerlin	_zDistort;	// Noise module that displaces the @a z coordinate.
		
		static const
			ESource		_sourceType = TURBULENCE;

	// methods
	public:
		CSourceTurbulence () :
			_power(DEFAULT_TURBULENCE_POWER)
		{
			SetSeed( DEFAULT_TURBULENCE_SEED );
			SetFrequency( DEFAULT_TURBULENCE_FREQUENCY );
			SetRoughness( DEFAULT_TURBULENCE_ROUGHNESS );
		}

		~CSourceTurbulence ()
		{}

		ESourceState GetState () const
		{
			return _source.IsNotNull() ? SOURCE_COMPLETE : UNCOMPLETE_ATTACHMENT_SOURCE;
		}
		
		virtual void Delete ()
		{
			_source = null;
			_xDistort.Delete();
			_yDistort.Delete();
			_zDistort.Delete();
		}

		virtual EResult GetInteger (ESourceQuery type, int *v, int count) const
		{
			CHECK_ARGUMENT_R( count >= 1, INVALID_VALUE );

			switch ( type )
			{
				case SOURCE_STATE :
					v[0] = GetState();
					return OK;

				case SOURCE_TYPE :
					v[0] = _sourceType;
					return OK;

				case SOURCES_COUNT : {
					int src_count = 0;
					_source->GetInteger( type, &src_count, 1 );
					v[0] = src_count + 1;
					return OK;
				}

				case INPUT_SOURCES :
					v[0] = 1;
					return OK;
			}
			return INVALID_ENUM;
		}

		virtual EResult SetArg (EArgument type, CSourcePtr src)
		{
			switch ( type )
			{
				case SOURCE_0 :	_source = src;	return OK;
			}
			return INVALID_ENUM;
		}

		virtual EResult SetArg (EArgument type, const real *v, int count)
		{
			CHECK_ARGUMENT_R( count >= 1, INVALID_VALUE );

			switch ( type )
			{
				case ARG_FREQUENCY :	SetFrequency( v[0] );	return OK;
				case ARG_POWER :		_power = v[0];			return OK;
			}
			return INVALID_ENUM;
		}

		virtual EResult SetArg (EArgument type, const int *v, int count)
		{
			CHECK_ARGUMENT_R( count >= 1, INVALID_VALUE );

			switch ( type )
			{
				case ARG_ROUGHNESS :	SetRoughness( v[0] );	return OK;
				case ARG_SEED :			SetSeed( v[0] );		return OK;
			}
			return INVALID_ENUM;
		}

		virtual EResult GetArg (EArgument type, CSourcePtr &src) const
		{
			switch ( type )
			{
				case SOURCE_0 :	src = _source;	return OK;
			}
			return INVALID_ENUM;
		}
		
		virtual EResult GetArg (EArgument type, real *v, int count) const
		{
			CHECK_ARGUMENT_R( count >= 1, INVALID_VALUE );

			switch ( type )
			{
				case ARG_FREQUENCY :	return _xDistort.GetArg( type, v, count );
				case ARG_POWER :		v[0] = _power;
			}
			return INVALID_ENUM;
		}
		
		virtual EResult GetArg (EArgument type, int *v, int count) const
		{
			CHECK_ARGUMENT_R( count >= 1, INVALID_VALUE );

			switch ( type )
			{
				case ARG_ROUGHNESS :	return _xDistort.GetArg( ARG_OCTAVE_COUNT, v, count );
				case ARG_SEED :			return _xDistort.GetArg( type, v, count );
			}
			return INVALID_ENUM;
		}

		void SetFrequency (real freq)
		{
			_xDistort.SetArg( ARG_FREQUENCY, &freq, 1 );
			_yDistort.SetArg( ARG_FREQUENCY, &freq, 1 );
			_zDistort.SetArg( ARG_FREQUENCY, &freq, 1 );
		}

		void SetSeed (int seed)
		{
			_xDistort.SetArg( ARG_SEED, &seed, 1 );	seed++;
			_yDistort.SetArg( ARG_SEED, &seed, 1 );	seed++;
			_zDistort.SetArg( ARG_SEED, &seed, 1 );
		}

		void SetRoughness (int roughness)
		{
			_xDistort.SetArg( ARG_OCTAVE_COUNT, &roughness, 1 );
			_yDistort.SetArg( ARG_OCTAVE_COUNT, &roughness, 1 );
			_zDistort.SetArg( ARG_OCTAVE_COUNT, &roughness, 1 );
		}
		
		virtual CSourcePtr Clone () const override
		{
			Self *	src = new Self();
			float	freq = 0.0f;
			int		seed = 0;
			int		roug = 0;

			_xDistort.GetArg( ARG_FREQUENCY, &freq, 1 );
			_xDistort.GetArg( ARG_SEED,		 &seed, 1 );
			_xDistort.GetArg( ARG_ROUGHNESS, &roug, 1 );

			src->_power = _power;
			src->SetFrequency( freq );
			src->SetRoughness( roug );
			src->SetSeed( seed );

			return src;
		}
		
		virtual void PreBuild (const TPreBuildData &data) override
		{
			data.Push( _source.ptr() );
			data.Push( &_xDistort );
			data.Push( &_yDistort );
			data.Push( &_zDistort );
		}

		virtual bool BuildFunction (TFunctionData &data) override
		{
			data.source
				<< "real " << CSource::FunctionName() << _id << " (real3 vPoint)\n{\n"
				<< "real3 p0 = vPoint + (real3)( 12414.0, 65124.0, 31337.0 ) / 65536.0;\n"
				<< "real _power = " << _power << ";\n"
				<< "real3 p1 = vPoint + (real3)( 26519.0, 18128.0, 60493.0 ) / 65536.0;\n"
				<< "real3 p2 = vPoint + (real3)( 53820.0, 11213.0, 44845.0 ) / 65536.0;\n"
				<< "real3 distort = (real3)( "
				<< CSource::FunctionName() << _xDistort.GetSourceId() << "( p0 ), "
				<< CSource::FunctionName() << _yDistort.GetSourceId() << "( p1 ), "
				<< CSource::FunctionName() << _zDistort.GetSourceId() << "( p2 ) ) "
				<< "* _power + vPoint;\n"
				<< "return " << CSource::FunctionName() << _source->GetSourceId() << "( distort );\n}\n";

			return true;
		}
		
		NATIVE(
		template < typename Real, Real (CSource::*NativeFunction)(const TVec<Real,3> &) >
		Real NativeFunction (const TVec<Real,3> &vPoint)
		{
			typedef TVec<Real,3>	Real3;

			Real3 p0 = vPoint + Real3( 12414.0, 65124.0, 31337.0 ) / 65536.0;
			Real3 p1 = vPoint + Real3( 26519.0, 18128.0, 60493.0 ) / 65536.0;
			Real3 p2 = vPoint + Real3( 53820.0, 11213.0, 44845.0 ) / 65536.0;

			Real3 distort = Real3(	(_xDistort.*NativeFunction)( p0 ),
									(_yDistort.*NativeFunction)( p1 ),
									(_zDistort.*NativeFunction)( p2 ) )
							* _power + vPoint;
		
			return (_source.ptr()->*NativeFunction)( distort );
		}

		virtual float32 NativeFunction32 (const fvec3 &vPoint) override
		{
			return NativeFunction< float32, &CSource::NativeFunction32 >( vPoint );
		}

		virtual float64 NativeFunction64 (const dvec3 &vPoint) override
		{
			return NativeFunction< float64, &CSource::NativeFunction64 >( vPoint );
		})

		static bool CreateSource (CSourcePtr &src, CContext *context, ESource type)
		{
			ASSERT( type == _sourceType );

			src = new Self();
			return true;
		}
	};

}	// Noise